#include "any.h"
#include "base.h"
#include "stddef.h"
#include "string.h"
#include "stdlib.h"
#include "stdio.h"
#include "assert.h"
#include "common.h"
#include "stmt.h"

#include <map>
#include <vector>

#define AS_DOUBLE

typedef std::map<any_t, any_t> dict_tree_t;

bool is_number(const any_t& a)
{
    return a.type >= atype_t::ANY_BOOL && a.type <= atype_t::ANY_DOUBLE;
}

const char* get_type_name(atype_t type)
{
    static const char* names[(int)atype_t::ANY_COUNT] = { nullptr };
    if (names[0] == nullptr) {
        names[(int)atype_t::ANY_VOID] = "void";
        names[(int)atype_t::ANY_NONE] = "none";
        names[(int)atype_t::ANY_BOOL] = "bool";
        names[(int)atype_t::ANY_CHAR] = "char";
        names[(int)atype_t::ANY_I8] = "int";
        names[(int)atype_t::ANY_DOUBLE] = "double";
        names[(int)atype_t::ANY_STRING] = "string";
        names[(int)atype_t::ANY_ARRAY] = "array";
        names[(int)atype_t::ANY_DICT] = "dict";
        names[(int)atype_t::ANY_PAIR] = "pair";
        names[(int)atype_t::ANY_REF] = "ref";
        names[(int)atype_t::ANY_CLASS] = "class";
        names[(int)atype_t::ANY_METHOD] = "method";
        for (int i = 0; i < (int)atype_t::ANY_COUNT; ++i) {
            assert(names[i] != nullptr);
        }
    }
    if ((int)type >= 0 && (int)type <= (int)atype_t::ANY_COUNT) {
        return names[(int)type];
    } else {
        return nullptr;
    }
}

any_t add_any(const any_t& a, const any_t& b)
{
    any_t ret;
    if (!is_number(a) || !is_number(b)) {
        if (a.type == atype_t::ANY_STRING && b.type == atype_t::ANY_STRING) {
            return any_t(atype_t::ANY_STRING, make_string("%s%s", a.s, b.s));
        } else if (a.type == atype_t::ANY_STRING) {
            return any_t(atype_t::ANY_I8, (int64_t)(strcmp(a.s, b.s) <= 0));
        }
    } else if (a.type == atype_t::ANY_DOUBLE || b.type == atype_t::ANY_DOUBLE) {
        ret.type = atype_t::ANY_DOUBLE;
        if (a.type == atype_t::ANY_I8) {
            ret.d = (double)a.i8 + b.d;
        } else if (b.type == atype_t::ANY_I8) {
            ret.d = (double)b.i8 + a.d;
        } else {
            ret.d = b.d + a.d;
        }
    } else {
        assert(a.type == atype_t::ANY_I8 && b.type == atype_t::ANY_I8);
        ret.type = atype_t::ANY_I8;
        ret.i8 = a.i8 + b.i8;
    }
    return ret;
}

any_t sub_any(const any_t& a, const any_t& b)
{
    any_t ret;
    if (a.type == atype_t::ANY_DOUBLE || b.type == atype_t::ANY_DOUBLE) {
        ret.type = atype_t::ANY_DOUBLE;
        if (a.type == atype_t::ANY_I8) {
            ret.d = (double)a.i8 - b.d;
        } else if (b.type == atype_t::ANY_I8) {
            ret.d = a.d - (double)b.i8;
        } else {
            ret.d = a.d - b.d;
        }
    } else {
        assert(a.type == atype_t::ANY_I8 && b.type == atype_t::ANY_I8);
        ret.type = atype_t::ANY_I8;
        ret.i8 = a.i8 - b.i8;
    }
    return ret;
}
any_t mul_any(const any_t& a, const any_t& b)
{
    any_t ret;
    if (a.type == atype_t::ANY_DOUBLE || b.type == atype_t::ANY_DOUBLE) {
        ret.type = atype_t::ANY_DOUBLE;
        if (a.type == atype_t::ANY_I8) {
            ret.d = (double)a.i8 * b.d;
        } else if (b.type == atype_t::ANY_I8) {
            ret.d = (double)b.i8 * a.d;
        } else {
            ret.d = b.d * a.d;
        }
    } else {
        assert(a.type == atype_t::ANY_I8 && b.type == atype_t::ANY_I8);
        ret.type = atype_t::ANY_I8;
        ret.i8 = a.i8 * b.i8;
    }
    return ret;
}
any_t div_any(const any_t& a, const any_t& b)
{
    any_t ret;
    if (a.type == atype_t::ANY_DOUBLE || b.type == atype_t::ANY_DOUBLE) {
        ret.type = atype_t::ANY_DOUBLE;
        if (a.type == atype_t::ANY_I8) {
            ret.d = (double)a.i8 / b.d;
        } else if (b.type == atype_t::ANY_I8) {
            ret.d = a.d / (double)b.i8;
        } else {
            ret.d = a.d / b.d;
        }
    } else {
        assert(a.type == atype_t::ANY_I8 && b.type == atype_t::ANY_I8);
        ret.type = atype_t::ANY_I8;
        ret.i8 = a.i8 / b.i8;
    }
    return ret;
}

any_t mod_any(const any_t& a, const any_t& b)
{
    any_t ret;
    if (a.type == atype_t::ANY_DOUBLE || b.type == atype_t::ANY_DOUBLE) {
        ret.type = atype_t::ANY_DOUBLE;
        if (a.type == atype_t::ANY_I8) {
            ret.d = (double)(a.i8 % (long long)b.d);
        } else if (b.type == atype_t::ANY_I8) {
            ret.d = (double)((long long)a.d % b.i8);
        } else {
            ret.d = (double)((long long)a.d % (long long)b.d);
        }
    } else {
        assert(a.type == atype_t::ANY_I8 && b.type == atype_t::ANY_I8);
        ret.type = atype_t::ANY_I8;
        ret.i8 = a.i8 % b.i8;
    }
    return ret;
}
any_t less_any(const any_t& a, const any_t& b)
{
    any_t ret(atype_t::ANY_BOOL, 0);
    if (!is_number(a) || !is_number(b)) {
        if (a.type != b.type) {
            ret.i8 = (int)(a.type < b.type);
        } else if (a.type == atype_t::ANY_STRING) {
            ret.i8 = (int)(strcmp(a.s, b.s) < 0);
        }
    } else if (a.type == atype_t::ANY_DOUBLE || b.type == atype_t::ANY_DOUBLE) {
        if (a.type == atype_t::ANY_I8) {
            ret.d = a.i8 < b.d;
        } else if (b.type == atype_t::ANY_I8) {
            ret.d = a.d < b.i8;
        } else {
            ret.d = a.d < b.d;
        }
    } else if (a.type == atype_t::ANY_CHAR || b.type == atype_t::ANY_CHAR) {
        ret.i8 = a.i8 < b.i8;
    } else {
        assert(a.type == atype_t::ANY_I8 && b.type == atype_t::ANY_I8);
        ret.i8 = a.i8 < b.i8;
    }
    return ret;
}
any_t equal_any(const any_t& a, const any_t& b)
{
    if (a.type == atype_t::ANY_NONE || b.type == atype_t::ANY_NONE)
        return { atype_t::ANY_BOOL, a.type == b.type ? 1 : 0 };
    any_t ret(atype_t::ANY_BOOL, 0);
    if (!is_number(a) || !is_number(b)) {
        if (a.type != b.type) {
            ret.i8 = 0;
        } else if (a.type == atype_t::ANY_STRING) {
            ret.i8 = (int)(strcmp(a.s, b.s) == 0);
        } else {
            ret.i8 = a.ref == b.ref;
        }
    } else if (a.type == atype_t::ANY_DOUBLE || b.type == atype_t::ANY_DOUBLE) {
        if (a.type == atype_t::ANY_I8) {
            ret.d = a.i8 == b.d;
        } else if (b.type == atype_t::ANY_I8) {
            ret.d = a.d == b.i8;
        } else {
            ret.d = a.d == b.d;
        }
    } else {
        ret.i8 = a.i8 == b.i8;
    }
    return ret;
}
any_t le_any(const any_t& a, const any_t& b)
{
    if (a.type == atype_t::ANY_NONE || b.type == atype_t::ANY_NONE)
        return { atype_t::ANY_BOOL, a.type == b.type ? 1 : 0 };
    any_t ret(atype_t::ANY_BOOL, 0);
    if (!is_number(a) || !is_number(b)) {
        if (a.type != b.type) {
            ret.i8 = (int)(a.type < b.type);
        } else if (a.type == atype_t::ANY_STRING) {
            ret.i8 = (int)(strcmp(a.s, b.s) <= 0);
        } else {
            ret.i8 = a.ref == b.ref;
        }
    } else if (a.type == atype_t::ANY_DOUBLE || b.type == atype_t::ANY_DOUBLE) {
        if (a.type == atype_t::ANY_I8) {
            ret.d = a.i8 <= b.d;
        } else if (b.type == atype_t::ANY_I8) {
            ret.d = a.d <= b.i8;
        } else {
            ret.d = a.d <= b.d;
        }
    } else {
        assert(a.type == atype_t::ANY_I8 && b.type == atype_t::ANY_I8);
        ret.i8 = a.i8 <= b.i8;
    }
    return ret;
}

any_t neg_any(const any_t& a)
{
    if (a.type == atype_t::ANY_I8 || a.type == atype_t::ANY_BOOL ||
        a.type == atype_t::ANY_CHAR ) {
        return { a.type, -a.i8 };
    } else if (a.type == atype_t::ANY_DOUBLE) {
        return { a.type, -a.d };
    } else {
        return any_t();
    }
}

any_t not_any(const any_t& a)
{
    if (a.type == atype_t::ANY_I8 || a.type == atype_t::ANY_BOOL ||
        a.type == atype_t::ANY_CHAR ) {
        return { atype_t::ANY_BOOL, !a.i8 };
    } else {
        return any_t();
    }
}
any_t equal_any(const any_t& a, int64_t i8)
{
    if (a.type == atype_t::ANY_NONE)
        return { atype_t::ANY_BOOL, 0 };
    if (a.type == atype_t::ANY_I8 || a.type == atype_t::ANY_BOOL ||
        a.type == atype_t::ANY_CHAR ) {
        return { a.type, (i8 == a.i8 ? 1 : 0) };
    } else if (a.type == atype_t::ANY_DOUBLE) {
        return { a.type, ((double)i8 == a.d ? 1 : 0) };
    } else {
        return any_t();
    }
}
any_t ne_any(const any_t& a, double d)
{
    if (a.type == atype_t::ANY_NONE)
        return { atype_t::ANY_BOOL, 1 };
    if (a.type == atype_t::ANY_I8 || a.type == atype_t::ANY_BOOL ||
        a.type == atype_t::ANY_CHAR ) {
        return { a.type, (d != (double)a.i8 ? 1 : 0) };
    } else if (a.type == atype_t::ANY_DOUBLE) {
        return { a.type, (d != a.d ? 1 : 0) };
    } else {
        return any_t();
    }
}
any_t ne_any(const any_t& a, int64_t i8)
{
    if (a.type == atype_t::ANY_NONE)
        return { atype_t::ANY_BOOL, 1 };
    if (a.type == atype_t::ANY_I8 || a.type == atype_t::ANY_BOOL ||
        a.type == atype_t::ANY_CHAR ) {
        return { a.type, (i8 != a.i8 ? 1 : 0) };
    } else if (a.type == atype_t::ANY_DOUBLE) {
        return { a.type, ((double)i8 != a.d ? 1 : 0) };
    } else {
        return any_t();
    }
}
any_t equal_any(const any_t& a, double d)
{
    if (a.type == atype_t::ANY_NONE)
        return { atype_t::ANY_BOOL, 0 };
    if (a.type == atype_t::ANY_I8 || a.type == atype_t::ANY_BOOL ||
        a.type == atype_t::ANY_CHAR ) {
        return { a.type, (d == (double)a.i8 ? 1 : 0) };
    } else if (a.type == atype_t::ANY_DOUBLE) {
        return { a.type, (d == a.d ? 1 : 0) };
    } else {
        return any_t();
    }
}

int fprint_any_d(FILE* f, const any_t& v)
{
    int ret = 0;
    if (v.type == atype_t::ANY_STRING) {
        ret += fprintf(f, "\"");
        ret += fprint_any(f, v);
        ret += fprintf(f, "\"");
    } else if (v.type == atype_t::ANY_CHAR) {
        ret += fprintf(f, "\'");
        ret += fprint_any(f, v);
        ret += fprintf(f, "\'");
    } else {
        ret += fprint_any(f, v);
    }
    return ret;
}

int fprint_any(FILE* f, const any_t& v)
{
    int ret = 0;
    if (v.type == atype_t::ANY_I8) {
        ret += fprintf(f, "%lld", (long long)v.i8);
    } else if (v.type == atype_t::ANY_DOUBLE) {
        ret += fprintf(f, "%f", v.d);
    } else if (v.type == atype_t::ANY_BOOL) {
        if (v.i8 != 0) {
            ret += fprintf(f, "true");
        } else {
            ret += fprintf(f, "false");
        }
    } else if (v.type == atype_t::ANY_VOID) {
        ret += fprintf(f, "void");
    } else if (v.type == atype_t::ANY_NONE) {
        ret += fprintf(f, "none");
    } else if (v.type == atype_t::ANY_STRING) {
        ret += fprintf(f, "%s", v.s);
    } else if (v.type == atype_t::ANY_CHAR) {
        ret += fprintf(f, "'%c'", (int)v.i8);
    } else if (v.type == atype_t::ANY_ARRAY) {
        ret += fprintf(f, "[ ");
        if (v.array) {
            for (int i = 0; i < v.array->get_count(); ++i) {
                if (i != 0)
                    ret += fprintf(f, ", ");
                auto e = v.array->get(i);
                if (e) {
                    ret += fprint_any_d(f, *e);
                } else {
                    ret += fprintf(f, "[nullptr]");
                }
            }
        }
        ret += fprintf(f, " ]");
    } else if (v.type == atype_t::ANY_DICT) {
        ret += v.dict->print(f, 0);
    } else if (v.type == atype_t::ANY_REF) {
        ret += fprintf(f, "ref to '");
        if (v.ref == nullptr) {
            ret += fprintf(f, "nullptr");
        } else {
            ret += fprint_any(f, *v.ref);
        }
        ret += fprintf(f, "'");
    } else if (v.type == atype_t::ANY_METHOD) {
        ret += fprintf(f, "method: %s", v.method->name);
    } else if (v.type == atype_t::ANY_CLASS) {
        ret += fprintf(f, "object: %s", v.obj->get_type()->name);
    } else {
        printf("unexpected any_t type: %s\n", get_type_name(v.type));
        assert(0);
    }
    return ret;
}

int print_any(const any_t& v)
{
    return fprint_any(stdout, v);
}

static bool operator < (const any_t& a1, const any_t& a2)
{
    return less_any(a1, a2).i8 != 0;
}

// use pointer to make memory leak checking clear
auto g_ref_p = new std::vector<ref_t*>;
auto& g_ref = *g_ref_p;

void ref_t::record()
{
    g_ref.push_back(this);
}

void ref_t::remove() const
{
    for (size_t i = 0; i != g_ref.size(); ++i) {
        g_ref.erase(g_ref.begin() + i);
        break;
    }
}

void ref_t::print_ref()
{
    printf("-----------------array ref--------------------\n");
    for (size_t i = 0; i != g_ref.size(); ++i) {
        printf("ref %d. ref = %d\n", (int)i, g_ref[i]->ref);
    }
    printf("\n");
    printf("-----------------array ref--------------------\n");
}


void any_t::free()
{
    if (type == atype_t::ANY_ARRAY) {
        if (array)
            array->free();
    }
    else if (type == atype_t::ANY_DICT) {
        if (dict)
            dict->free();
    }
    else if (type == atype_t::ANY_PAIR) {
        if (pair) {
            pair->first.free();
            pair->second.free();
        }
    }
    else if (type == atype_t::ANY_CLASS) {
        if (obj)
            obj->free();
    }
    else if (type == atype_t::ANY_METHOD) {
    }
    else if (type == atype_t::ANY_STRING) {
    }
}

auto g_array_p = new std::vector<array_t*>;
auto& g_array = *g_array_p;

void array_t::record()
{
    ref_t::record();
    g_array.push_back(this);
}

void array_t::remove() const
{
    ref_t::remove();
    for (size_t i = 0; i != g_array.size(); ++i) {
        g_array.erase(g_array.begin() + i);
        break;
    }
}

void array_t::print_ref()
{
    printf("\n");
    printf("-----------------ref-------------------->\n");
    printf("\n");
    for (size_t i = 0; i != g_array.size(); ++i) {
        printf("array %d. ref = %d: ", (int)i, g_array[i]->ref);
        any_t a(atype_t::ANY_ARRAY, (array_t*)g_array[i]);
        fprint_any_d(stdout, a);
        printf("\n");
    }
    printf("\n");
}

auto g_dict_p = new std::vector<dict_t*>;
auto& g_dict = *g_dict_p;

void dict_t::record()
{
    ref_t::record();
    g_dict.push_back(this);
}

void dict_t::remove() const
{
    ref_t::remove();
    for (size_t i = 0; i != g_dict.size(); ++i) {
        g_dict.erase(g_dict.begin() + i);
        break;
    }
}

void dict_t::print_ref()
{
    printf("\n");
    for (size_t i = 0; i != g_dict.size(); ++i) {
        printf("dict %d. ref = %d:\n", (int)i, g_dict[i]->ref);
        any_t a(atype_t::ANY_DICT, (dict_t*)g_dict[i]);
        fprint_any_d(stdout, a);
    }
    printf("\n");
    printf("<-----------------ref---------------------\n");
}

any_t* dict_t::find(const any_t& key) const
{
    if (tree == nullptr)
        return nullptr;
    auto map = (dict_tree_t*)tree;
    auto iter = map->find(key);
    if (iter != map->end()) {
        return &iter->second;
    }
    else {
        return nullptr;
    }
}

int dict_t::get_count() const
{
    if (tree == nullptr) {
        return 0;
    }
    auto map = (dict_tree_t*)tree;
    return (int)map->size();
}

bool dict_t::insert(const any_t& key, const any_t& value)
{
    if (tree == nullptr) {
        tree = new dict_tree_t;
        if (tree == nullptr)
            return false;
    }
    auto map = (dict_tree_t*)tree;
    return map->emplace(key, value).second;
}

static int fprint_tab(FILE* f, int tab)
{
    int ret = 0;
    for (int i = 0; i < tab; ++i)
    {
        ret += fprintf(f, "    ");
    }
    return ret;
}

int dict_t::print(FILE* f, int tab) const
{
    int ret = 0;
    ret += fprint_tab(f, tab);
    if (this->tree == nullptr ||
        ((std::map<any_t, any_t>*)this->tree)->size() == 0) {
        ret += fprintf(f, "{  }");
        return ret;
    }
    ret += fprintf(f, "{\n");
    auto tree = (std::map<any_t, any_t>*)this->tree;
    for (auto iter : *tree) {
        ret += fprint_tab(f, tab + 1);
        ret += fprintf(f, "(key: ");
        ret += fprint_any_d(f, iter.first);
        ret += fprintf(f, " , value: ");
        ret += fprint_any_d(f, iter.second);
        ret += fprintf(f, " )\n");
    }
    ret += fprint_tab(f, tab);
    ret += fprintf(f, "}\n");
    return ret;
}

dict_t::~dict_t()
{
    delete (dict_tree_t*)tree;
}

void dict_t::free()
{
}

void add_ref(any_t* a)
{
    if (a->type == atype_t::ANY_ARRAY) {
        if (a->array) {
            ++a->array->ref;
        }
    }
    else if (a->type == atype_t::ANY_DICT) {
        if (a->dict) {
            ++a->dict->ref;
        }
    }
    else if (a->type == atype_t::ANY_CLASS) {
        if (a->obj) {
            ++a->obj->ref;
        }
    }
}

void sub_ref(any_t* a)
{
    if (a->type == atype_t::ANY_ARRAY) {
        assert(a->array);
        if (a->array) {
            --a->array->ref;
            auto array = a->array;
            if (array->ref <= 0) {
                for (int i = 0; i < array->get_count(); ++i) {
                    sub_ref(array->get(i));
                }
            }
        }
    }
    else if (a->type == atype_t::ANY_DICT) {
        if (a->dict) {
            --a->dict->ref;
            auto dict = a->dict;
            auto tree = (dict_tree_t*)dict->tree;
            if (dict->ref <= 0 && tree) {
                for (auto& pair : *tree) {
                    sub_ref((any_t*)&pair.first);
                    sub_ref((any_t*)&pair.second);
                }
            }
        }
    }
    else if (a->type == atype_t::ANY_CLASS) {
        if (a->obj) {
            --a->obj->ref;
            auto obj = a->obj;
            if (obj->ref <= 0) {
                for (int i = 0; i < obj->get_count(); ++i) {
                    sub_ref(obj->get(i));
                }
            }
        }
    }
}

void try_free_ref(any_t* a)
{
    if (a->type == atype_t::ANY_ARRAY) {
        assert(a->array);
        if (a->array) {
            auto array = a->array;
            assert(array->ref >= 0);
            if (array->ref <= 0) {
                for (int i = 0; i < array->get_count(); ++i) {
                    sub_ref(array->get(i));
                }
                for (int i = 0; i < array->get_count(); ++i) {
                    try_free_ref(array->get(i));
                }
                delete a->array;
                a->array = nullptr;
            }
        }
    }
    else if (a->type == atype_t::ANY_DICT) {
        if (a->dict) {
            if (a->dict->ref == 0) {
                auto tree = (dict_tree_t*)a->dict->tree;
                if (tree) {
                    for (auto& pair : *tree) {
                        sub_ref((any_t*)&pair.first);
                        sub_ref((any_t*)&pair.second);
                    }
                    for (auto& pair : *tree) {
                        try_free_ref((any_t*)&pair.first);
                        try_free_ref((any_t*)&pair.second);
                    }
                }
                delete a->dict;
                a->dict = nullptr;
            }
        }
    }
    else if (a->type == atype_t::ANY_CLASS) {
        assert(a->obj);
        if (a->obj) {
            auto array = a->obj;
            assert(array->ref >= 0);
            if (array->ref <= 0) {
                for (int i = 0; i < array->get_count(); ++i) {
                    sub_ref(array->get(i));
                }
                for (int i = 0; i < array->get_count(); ++i) {
                    try_free_ref(array->get(i));
                }
                delete a->obj;
                a->obj = nullptr;
            }
        }
    }
}

int get_ref(any_t* a)
{
    if (a->type == atype_t::ANY_ARRAY) {
        assert(a->array);
        if (a->array) {
            return a->array->ref;
        }
    }
    else if (a->type == atype_t::ANY_DICT) {
        if (a->dict) {
            return a->dict->ref;
        }
    }
    return 1;
}

bool is_no_ref(any_t* a)
{
    if (a->type == atype_t::ANY_ARRAY) {
        assert(a->array);
        if (a->array) {
            return a->array->ref == 0;
        }
    }
    else if (a->type == atype_t::ANY_DICT) {
        if (a->dict) {
            return a->dict->ref == 0;
        }
    }
    return false;
}

static auto g_string_obj = class_obj_t::create(0, nullptr);

class_obj_t* any_t::get_obj()
{
    if (type == atype_t::ANY_STRING) {
        return g_string_obj;
    } else {
        assert(type == atype_t::ANY_ARRAY
            || type == atype_t::ANY_DICT
            || type == atype_t::ANY_CLASS
        );
        return obj;
    }
}

auto g_objects_p = new std::vector<class_obj_t*>;
auto& g_objects = *g_objects_p;

void class_obj_t::record()
{
    ref_t::record();
    g_objects.push_back(this);
}

void class_obj_t::remove() const
{
    ref_t::remove();
    for (size_t i = 0; i != g_objects.size(); ++i) {
        if (g_objects[i] == this)
            g_objects.erase(g_objects.begin() + i);
        break;
    }
}

field_t* class_obj_t::get_field(const char* s)
{
    assert(type);
    return type->get_field(s);
}
int class_obj_t::get_field_index(const char* s)
{
    assert(type);
    return type->get_field_index(s);
}
any_t* class_obj_t::get(const char* s)
{
    assert(type);
    int i = type->get_field_index(s);
    if (i < 0 || i >= count)
        return nullptr;
    else
        return ptr + i;
}
void class_obj_t::set(const char* s, const any_t& a)
{
    assert(type);
    int i = type->get_field_index(s);
    assert(i >= 0 && i < count);
    if (i >= 0 && i < count)
        ptr[i] = a;
}

method_t* class_obj_t::get_method(const char* s, int count)
{
    assert(type);
    return type->get_method(s, count);
}

void class_obj_t::print_ref()
{
    printf("\n");
    printf("-----------------ref-------------------->\n");
    printf("\n");
    for (size_t i = 0; i != g_objects.size(); ++i) {
        printf("array %d. ref = %d: ", (int)i, g_objects[i]->ref);
        any_t a(atype_t::ANY_CLASS, (class_obj_t*)g_objects[i]);
        fprint_any_d(stdout, a);
        printf("\n");
    }
    printf("\n");
}

void print_ref()
{
    array_t::print_ref();
    dict_t::print_ref();
    class_obj_t::print_ref();
}

void free_all_ref()
{
    for (size_t i = 0; i != g_array.size(); ++i) {
        delete g_array[i];
    }
    for (size_t i = 0; i != g_dict.size(); ++i) {
        delete g_dict[i];
    }
    for (size_t i = 0; i != g_objects.size(); ++i) {
        delete g_objects[i];
    }
    delete g_ref_p;
    delete g_array_p;
    delete g_dict_p;
    delete g_objects_p;
    delete g_string_obj;
}

int g_any_t_id;
